/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.service.paste;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.constant.knowledge.AssetClassification;
import cn.ac.nercis.pes.common.constant.knowledge.VirtualClassification;
import cn.ac.nercis.pes.common.constant.project.SpecialType;
import cn.ac.nercis.pes.model.dto.common.DataCopy;
import cn.ac.nercis.pes.model.dto.common.DataPaste;
import cn.ac.nercis.pes.model.dto.common.ItemData;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.evaluation.onsite.EvaluationRecordDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemSpecialTargetRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.evaluation.EvaluationRangeService;
import cn.ac.nercis.pes.service.evaluation.EvaluationRecordService;
import cn.ac.nercis.pes.service.evaluation.EvaluationRequireService;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import cn.ac.nercis.pes.service.utils.FileManage;
import cn.ac.nercis.pes.service.utils.ItemUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 现场测评阶段相关数据粘贴服务类
 *
 * @author limy
 * @version 1.0.0
 */
@Slf4j
@Service
public record OnsitePasteService(EvaluationRequireService requireService,
                                 EvaluationRangeService evaluationRangeService,
                                 EvaluationRecordService evaluationRecordService,
                                 AssetRepository assetRepository,
                                 AssetEvaluationMethodRepository evaluationMethodRepository,
                                 EvaluationRecordRepository evaluationRecordRepository,
                                 SystemTargetRepository systemTargetRepository,
                                 SystemSpecialTargetRepository systemSpecialTargetRepository) {

    @Autowired
    public OnsitePasteService {
    }

    public Result<String> evalDataPaste(DataPaste dataPaste, DataCopy dataCopy) {
        if (StringUtils.equals(dataCopy.getName(), dataPaste.getName())
                && StringUtils.equals(dataCopy.getData(), dataPaste.getData())
                && StringUtils.equals(dataCopy.getCopySystemId(), dataPaste.getSystemId())) {
            return Result.success();
        }
        var virtualClass = Stream.of(VirtualClassification.values())
                .filter(vc -> StringUtils.endsWith(dataPaste.getName(), vc.getName())).findFirst()
                .orElse(null);
        if (Objects.isNull(virtualClass)) {
            return Result.failed("粘贴失败，未找到对应的虚拟系统资产分类");
        }
        if (StringUtils.isNotBlank(dataPaste.getData())) {
            return evalDataPasteByAsset(virtualClass, dataPaste.getData(), dataCopy.getData());
        } else {
            return evalDataPasteByVirtualClass(dataCopy.getCopySystemId(), dataPaste.getSystemId(), virtualClass);
        }
    }


    public Result<String> evalDataPasteByVirtualClass(String copySystemId, String systemId, VirtualClassification virtualClass) {
        var copyAssets = assetRepository.findAllBySystemIdAndClassIdIn(copySystemId,Set.copyOf(virtualClass.getClassIds()))
                .stream()
                .filter(AssetDO::getIsUse)
                .toList();
        var pastAssets = assetRepository.findAllBySystemIdAndClassIdIn(systemId,Set.copyOf(virtualClass.getClassIds()))
                .stream()
                .filter(AssetDO::getIsUse)
                .toList();
        for(var copyAsset:copyAssets){
           var pastAsset = pastAssets.stream()
                   .filter(a->StringUtils.equals(AssetUtils.getAssetName(copyAsset),AssetUtils.getAssetName(a)))
                   .findFirst()
                   .orElse(null);
           if(Objects.isNull(pastAsset)){
               continue;
           }
            pasteAssetEvalRecord(pastAsset, copyAsset);
        }
        return Result.success();
    }

    public Result<String> evalDataPasteByAsset(VirtualClassification vc, String pasteAssetId, String copyAssetId) {
        //获取粘贴资产对象信息
        var pasteAsset = assetRepository.findById(pasteAssetId).orElse(null);
        if (Objects.isNull(pasteAsset)) {
            return Result.failed("未找到粘贴的资产对象信息");
        }
        //复制的资产对象信息
        var copyAsset = assetRepository.findById(copyAssetId).orElse(null);
        if (Objects.isNull(copyAsset)) {
            return Result.failed("未找到复制的资产对象信息");
        }
        return dataPasteByAsset(pasteAsset, List.of(copyAsset), vc);
    }

    private Result<String> dataPasteByAsset(AssetDO pasteAsset, List<AssetDO> copyAssets, VirtualClassification vc) {
        AssetDO copyAsset = copyAssets.stream().findFirst().orElse(null);
        if (Objects.isNull(copyAsset)) {
            return Result.failed("未找到复制的资产对象信息");
        }
        switch (vc) {
            case PHY_SECURITY -> {
                return pasteAssetEvalRecordByParent(pasteAsset, copyAsset, List.of(AssetClassification.PHY_SECURITY.getId()));
            }
            case APP_SECURITY -> {
                return pasteAssetEvalRecordByParent(pasteAsset, copyAsset, List.of(AssetClassification.SUB_BUSINESS.getId(),AssetClassification.KEY_DATA.getId()));
            }
            default -> {
                return pasteAssetEvalRecord(pasteAsset, copyAsset);
            }
        }
    }

    private Result<String> pasteAssetEvalRecord(AssetDO pasteAsset, AssetDO copyAsset) {
        var pastRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(pasteAsset.getSystemId(),pasteAsset);
        var copyRecords = evaluationRecordRepository.findAllBySystemIdAndAsset(copyAsset.getSystemId(),copyAsset);
        if(CollectionUtils.isEmpty(copyRecords)){
            return Result.failed("复制的系统资产无测评记录信息！");
        }
        var copyTargets = findTargetByRecord(copyAsset.getSystemId(),copyRecords);
        var pastTargets = findTargetByAsset(pasteAsset.getSystemId(),List.of(pasteAsset));
        List<File> photoFiles = FileManage.findPhotoFiles(copyAsset.getSystemId());
        for(var copyRecord : copyRecords){
            var pastRecord = getPastEvalRecord(copyRecord,copyTargets,pastRecords,pastTargets,true);
            if(Objects.isNull(pastRecord)){
                createPasteAssetEvalRecordData(copyRecord,copyTargets,List.of(pasteAsset),pastTargets,photoFiles);
            }else {
                modifyPasteAssetEvalRecordData(copyRecord,pastRecord,photoFiles);
            }
        }
        return Result.success();
    }

    private Result<String> pasteAssetEvalRecordByParent(AssetDO pasteAsset, AssetDO copyAsset, List<String> subAssetClassIds) {
        var subPastAsset = assetRepository.findAllBySystemIdAndClassIdIn(pasteAsset.getSystemId(), Set.copyOf(subAssetClassIds))
                .stream()
                .filter(a->StringUtils.contains(a.getQuoteAssetId(), pasteAsset.getId())
                        && a.getIsUse())
                .toList();
        var subCopyAsset = assetRepository.findAllBySystemIdAndClassIdIn(copyAsset.getSystemId(), Set.copyOf(subAssetClassIds))
                .stream()
                .filter(a->StringUtils.contains(a.getQuoteAssetId(), copyAsset.getId())
                        && a.getIsUse())
                .toList();
        var pastRecords = evaluationRecordRepository.findAllBySystemId(pasteAsset.getSystemId())
                .stream()
                .filter(er->subPastAsset.stream().anyMatch(asset->StringUtils.equals(er.getAsset().getId(),asset.getId())))
                .toList();
        var copyRecords = evaluationRecordRepository.findAllBySystemId(copyAsset.getSystemId())
                .stream()
                .filter(er->subCopyAsset.stream().anyMatch(asset->StringUtils.equals(er.getAsset().getId(),asset.getId())))
                .toList();
        if(CollectionUtils.isEmpty(copyRecords)){
            return Result.failed("复制的系统资产无测评记录信息！");
        }
        var copyTargets = findTargetByRecord(copyAsset.getSystemId(),copyRecords);
        var pastTargets = findTargetByAsset(pasteAsset.getSystemId(),subPastAsset);
        List<File> photoFiles = FileManage.findPhotoFiles(copyAsset.getSystemId());
        for(var copyRecord : copyRecords){
            var pastRecord = getPastEvalRecord(copyRecord,copyTargets,pastRecords,pastTargets,false);
            if(Objects.isNull(pastRecord)){
                createPasteAssetEvalRecordData(copyRecord,copyTargets,subPastAsset,pastTargets,photoFiles);
            }else {
                modifyPasteAssetEvalRecordData(copyRecord,pastRecord,photoFiles);
            }
        }
        return Result.success();
    }

    private EvaluationRecordDO getPastEvalRecord(EvaluationRecordDO copyRecord, List<ItemData> copyTargets,
                                                 List<EvaluationRecordDO> pastRecords, List<ItemData> pastTargets, boolean isSingleAsset){
        var copyAssetName = copyRecord.getAsset().getName();
        var copyUnitTarget = ItemUtils.getItemNameByValue(copyTargets, copyRecord.getUnitTargetId());
        if(StringUtils.isBlank(copyUnitTarget)){
            return null;
        }
        if(isSingleAsset){
            return pastRecords.stream()
                    .filter(r-> StringUtils.equals(ItemUtils.getItemNameByValue(pastTargets,r.getUnitTargetId()),copyUnitTarget))
                    .findFirst()
                    .orElse(null);
        }else{
            return pastRecords.stream()
                    .filter(r->StringUtils.equals(ItemUtils.getItemNameByValue(pastTargets,r.getUnitTargetId()),copyUnitTarget)
                            && StringUtils.equals(r.getAsset().getName(), copyAssetName))
                    .findFirst()
                    .orElse(null);
        }
    }

    private void createPasteAssetEvalRecordData(EvaluationRecordDO copyRecord,List<ItemData> copyTargets,
                                                List<AssetDO> pastAssets, List<ItemData> pastTargets,
                                                List<File> photoFiles){
        var pastAsset = pastAssets.stream()
                .filter(a->StringUtils.equals(copyRecord.getAsset().getName(),a.getName()))
                .findFirst().orElse(null);
        var copyUnitTargetName = ItemUtils.getItemNameByValue(copyTargets, copyRecord.getUnitTargetId());
        var pastUnitTargetId = ItemUtils.getItemValueByName(pastTargets,copyUnitTargetName);
        if(Objects.isNull(pastAsset) || StringUtils.isBlank(pastUnitTargetId)){
            return;
        }
        var evalMethod = evaluationMethodRepository.findAllBySystemIdAndAssetId(pastAsset.getSystemId(),pastAsset.getId())
                .stream()
                .filter(em->StringUtils.equals(em.getUnitTargetId(),pastUnitTargetId))
                .findFirst()
                .orElse(null);
        if(Objects.isNull(evalMethod)){
            return;
        }
        var isApply = systemTargetRepository.findBySystemIdAndKmTargetId(pastAsset.getSystemId(),evalMethod.getUnitTargetId())
                .map(SystemTargetDO::getIsApply)
                .stream()
                .findFirst()
                .orElse(true);
        var pastEvalRecord = EvaluationRecordDTO.builder()
                .systemId(pastAsset.getSystemId())
                .assetId(pastAsset.getId())
                .layerTargetId(evalMethod.getLayerTargetId())
                .unitTargetId(evalMethod.getUnitTargetId())
                .assetEvaMethodId(evalMethod.getId())
                .refType(evalMethod.getRefType())
                .isApply(isApply)
                .build();
        applyAssetEvalRecord(copyRecord,pastEvalRecord);
        var pastResult = evaluationRecordService.saveAssetEvaluationRecord(pastEvalRecord);
        if (pastResult.isSuccess()) {
            evaluationRecordService.setRecordFinishState(pastResult.getData().getId(), copyRecord.getFinishState());
            pasteAssetEvalRecordFile(copyRecord, pastResult.getData(), photoFiles);
        }
    }

    private void applyAssetEvalRecord(EvaluationRecordDO copyRecord,EvaluationRecordDTO pastRecord){
        pastRecord.setConclusion(copyRecord.getConclusion());
        pastRecord.setDResult(copyRecord.getDResult());
        pastRecord.setAResult(copyRecord.getAResult());
        pastRecord.setKResult(copyRecord.getKResult());
        pastRecord.setDContent(copyRecord.getDContent());
        pastRecord.setAContent(copyRecord.getAContent());
        pastRecord.setKContent(copyRecord.getKContent());
        pastRecord.setContent(copyRecord.getContent());
        pastRecord.setDFiles(copyRecord.getDFiles());
        pastRecord.setAFiles(copyRecord.getAFiles());
        pastRecord.setKFiles(copyRecord.getKFiles());
        pastRecord.setManageFiles(copyRecord.getManageFiles());
        pastRecord.setScore(copyRecord.getScore());
    }

    private void pasteAssetEvalRecordFile(EvaluationRecordDO copyRecord, EvaluationRecordDTO pastRecord,List<File> photoFiles){
        Stream.of(copyRecord.getDFiles(),copyRecord.getAFiles(),copyRecord.getKFiles(),copyRecord.getManageFiles())
                .filter(StringUtils::isNotBlank)
                .flatMap(f-> Arrays.stream(StringUtils.split(f, BaseConstants.MULTI_SPLIT_CHAR)))
                .map(pictureName->photoFiles.stream().filter(f->StringUtils.equals(f.getName(),pictureName))
                        .findFirst().orElse(null))
                .filter(Objects::nonNull)
                .forEach(copyFile->{
                    var layerTargetName = copyFile.getParentFile().getName();
                    var pastFile = FileUtils.getFile(FileManage.getPhotoFileDir(pastRecord.getSystemId(),layerTargetName),copyFile.getName());
                    try{
                        FileManage.copyFile(copyFile,pastFile);
                    }catch (IOException ex){
                        log.warn("粘贴证据文件出错：",ex);
                    }
                });
    }

    private void modifyPasteAssetEvalRecordData(EvaluationRecordDO copyRecord, EvaluationRecordDO pastRecord,List<File> photoFiles){
        var isApply = systemTargetRepository.findBySystemIdAndKmTargetId(pastRecord.getSystemId(),pastRecord.getUnitTargetId())
                .map(SystemTargetDO::getIsApply)
                .stream()
                .findFirst()
                .orElse(true);
        var pastEvalRecord = EvaluationRecordDTO.builder()
                .id(pastRecord.getId())
                .systemId(pastRecord.getSystemId())
                .assetId(pastRecord.getAsset().getId())
                .layerTargetId(pastRecord.getLayerTargetId())
                .unitTargetId(pastRecord.getUnitTargetId())
                .assetEvaMethodId(pastRecord.getAssetEvaMethodId())
                .refType(pastRecord.getRefType())
                .isApply(isApply)
                .build();
        applyAssetEvalRecord(copyRecord,pastEvalRecord);
        var pastResult = evaluationRecordService.saveAssetEvaluationRecord(pastEvalRecord);
        if (pastResult.isSuccess()) {
            evaluationRecordService.setRecordFinishState(pastResult.getData().getId(), copyRecord.getFinishState());
            pasteAssetEvalRecordFile(copyRecord, pastResult.getData(), photoFiles);
        }
    }

    private List<ItemData> findTargetByRecord(String systemId,List<EvaluationRecordDO> records){
        Set<String> unitTargetIds = records.stream().map(EvaluationRecordDO::getUnitTargetId).collect(Collectors.toSet());
        return findTargets(systemId,unitTargetIds);
    }

    private List<ItemData> findTargetByAsset(String systemId,List<AssetDO> subPastAsset){
        Set<String> subPastAssetIds = subPastAsset.stream().map(AssetDO::getId).collect(Collectors.toSet());
        Set<String> unitTargetIds = evaluationMethodRepository.findAllBySystemIdAndAssetIdIn(systemId,subPastAssetIds)
                .stream()
                .map(AssetEvaluationMethodDO::getUnitTargetId)
                .collect(Collectors.toSet());
        return findTargets(systemId,unitTargetIds);
    }

    private List<ItemData> findTargets(String systemId,Set<String> unitTargetIds){
        var systemTargets = this.systemTargetRepository.findAllBySystemIdAndKmTargetIdIn(systemId,unitTargetIds)
                .stream()
                .map(st->new ItemData(st.getKmName(),st.getKmTargetId()))
                .toList();
        var specialTargets = this.systemSpecialTargetRepository.findAllByIdIn(unitTargetIds)
                .stream()
                .filter(st->st.getSpecialType() != SpecialType.UPGRADE)
                .map(st->new ItemData(st.getName(),st.getId()))
                .toList();
        return Stream.concat(systemTargets.stream(),specialTargets.stream()).toList();
    }
}